home *** CD-ROM | disk | FTP | other *** search
/ Turnbull China Bikeride / Turnbull China Bikeride - Disc 2.iso / STUTTGART / LANG / C / LIB / DESK / CORE / Desk / h_doc / JumpRaw < prev    next >
Text File  |  1996-08-16  |  11KB  |  462 lines

  1. /*
  2.     ####             #    #     # #
  3.     #   #            #    #       #          The FreeWare C library for
  4.     #   #  ##   ###  #  # #     # ###             RISC OS machines
  5.     #   # #  # #     # #  #     # #  #   ___________________________________
  6.     #   # ####  ###  ##   #     # #  #
  7.     #   # #        # # #  #     # #  #    Please refer to the accompanying
  8.     ####   ### ####  #  # ##### # ###    documentation for conditions of use
  9.     ________________________________________________________________________
  10.  
  11.     File:    Jump.h
  12.     Author:  Copyright © 1995 Julian Smith
  13.     Version: 1.00 (26 Jan 1996)
  14.     Purpose: Provides try..throw..catch system without pulling in DeskLib's
  15.              Error2 header etc (unless compiling for SDLS.
  16. */
  17.  
  18. #ifndef __Desk_JumpRaw_h
  19. #define __Desk_JumpRaw_h
  20.  
  21. #ifdef __cplusplus
  22.     extern "C" {
  23. #endif
  24.  
  25. #include <setjmp.h>
  26.  
  27.  
  28. #ifndef __Desk_Debug_h
  29.     #include "Desk.Debug.h"
  30. #endif
  31.  
  32.  
  33.  
  34. /*
  35. This header defines two sets of macros, Desk_Jump_* and Desk_JumpAuto_* . There
  36. are rather a lot of macros, so that you can choose to use them in the
  37. way you want, rather than be forced to write things in a particular way.
  38.  
  39. Probably the most useful are the three Desk_JumpAuto_Try, Desk_JumpAuto_Catch and
  40. Desk_JumpAuto_EndCatch macros, or the Desk_JumpAuto_TryCatch macro.
  41.  
  42.  
  43.  
  44. Desk_Jump_ macros
  45. ------------
  46.  
  47. The macros Desk_Jump_* are for SDLS-happy set/longjmp-ing. They behave like
  48. setjmp / longjmp etc in normal compilation, but do a few extra things
  49. when compilation is for a SDLS, because the SDLS needs to know about
  50. long-jmping in order to keep an internal stack up-to-date.
  51.  
  52. See Desk_Jump_SetJmp for more information.
  53.  
  54.  
  55.  
  56.  
  57. Desk_JumpAuto_ macros
  58. ----------------
  59.  
  60. The macros Desk_JumpAuto_* can be used in a similar way, except that they
  61. also automatically define and use local variables to effectively
  62. maintain a stack of jmp_buf's. This allows a function to call
  63. 'Desk_JumpAuto_Throw( val) instead of Desk_Jump_LongJmp( buf, val), ie without
  64. having been passed a jmp_buf explicitly.
  65.  
  66. This means you don't have to pass a (jmp_buf *) to every function which
  67. might want to call 'longjmp'.
  68.  
  69.  
  70. To make things even simpler, you can use the Desk_JumpAuto_Try,
  71. Desk_JumpAuto_Catch, Desk_JumpAuto_EndCatch or Desk_JumpAuto_TryCatch macros.
  72.  
  73.  */
  74.  
  75.  
  76.  
  77.  
  78.  
  79.  
  80. /* Desk_Jump_*    */
  81.  
  82. typedef struct    {
  83.     jmp_buf    jmpbuf;
  84.     
  85.     #ifdef Desk__using_SDLS
  86.         int    Desk_jump__sdls_stackptr;
  87.     #endif
  88.     }
  89.     Desk_jump_buf;
  90. /*
  91. This is identical to the normal jmp_buf when compilation is for static
  92. linking. With SDLS, an extra field is present which is needed by the
  93. SDLS.
  94.  */
  95.  
  96.  
  97.  
  98. #ifdef Desk__using_SDLS
  99.     
  100.     #include "Desk.Core.h"
  101.     
  102.     #define    Desk_Jump_SetJmp( buf)    ((buf).Desk_jump__sdls_stackptr=_dll_setjmp(), setjmp( (buf).jmpbuf))
  103.     /*
  104.     int    Desk_Jump_SetJmp( Desk_jump_buf buf);
  105.      */
  106.     
  107.     #define    Desk_Jump_ReceiveLongJmp( buf)    _dll_longjmped( (buf).Desk_jump__sdls_stackptr)
  108.     /*
  109.     void    Desk_Jump_ReceiveLongJmp( Desk_jump_buf buf);
  110.      */
  111.  
  112. #else
  113.  
  114.     #define    Desk_Jump_SetJmp( buf)    setjmp( (buf).jmpbuf)
  115. /*
  116. int    Desk_Jump_SetJmp( Desk_jump_buf buf);
  117.  
  118. SDLS-compatible version of 'setjmp'.
  119.  
  120. Instead of:
  121.  
  122.     jmp_buf    buf;
  123.     void Bar( void)    { if (error) longjmp( buf, 1);    }    
  124.     void Foo( void)
  125.     {
  126.     if ( !setjmp( buf))    { ... Bar(); ...    }
  127.     else            |* Handler error *|    }
  128.     }
  129.     
  130. do:
  131.  
  132.     Desk_jump_buf    buf;
  133.     void Bar( void)    { if (error) Desk_Jump_LongJmp( buf, 1);    }    
  134.     void Foo( void)
  135.     {
  136.     if ( !Desk_Jump_SetJmp( buf))    { ... Bar(); ...    }
  137.     else    { 
  138.         Desk_Jump_ReceiveLongJmp( buf); 
  139.         |* Handle error *|
  140.         }
  141.     }
  142.  
  143. Ie. Desk_Jump_SetJmp and Desk_Jump_LongJmp are drop-in replacements for setjmp
  144. and longjmp. Desk_Jump_ReceiveLongJmp() should be called immediately after a
  145. longjmp happens, and Desk_jump_buf should be used instead of jmp_buf, to
  146. define a jmp_buf.
  147.  */
  148.     
  149.     #define    Desk_Jump_ReceiveLongJmp( buf)
  150. /*
  151. Macro to call when a longjmp is received, for compatibility with SDLS.
  152.  
  153. See Desk_Jump_SetJmp.
  154.  */
  155.  
  156. #endif
  157.  
  158.  
  159. #define    Desk_Jump_LongJmp( buf, val)        longjmp( (buf).jmpbuf, val)
  160. /*
  161. void    Desk_Jump_LongJmp( Desk_jump_buf buf, int val);
  162.  
  163. SDLS-compatible version of 'setjmp'. 
  164.  
  165. Actually, as you can see, this macro is identical in SDLS and non-SDLS
  166. builds. It is included so that one can use 'Desk_Jump_' macros for
  167. everything.
  168.  
  169. See Desk_Jump_SetJmp.
  170.  */
  171.  
  172.  
  173.  
  174.  
  175.  
  176.  
  177.  
  178.  
  179.  
  180.  
  181.  
  182.  
  183.  
  184.  
  185.  
  186. /* Desk_JumpAuto_*    */
  187.  
  188. typedef struct    Desk_jumpauto_buf    {
  189.     Desk_jump_buf        jumpbuf;
  190.     struct Desk_jumpauto_buf*    previous;
  191.     }
  192.     Desk_jumpauto_buf;
  193. /*
  194. This contains all information needed to allow the JumpAuto system to
  195. keep track of a stack of Desk_jump_buf's.
  196.  */
  197.  
  198.  
  199. #ifdef Desk__using_SDLS
  200.     extern Desk_jumpauto_buf**    Desk_Jump__Ref_autonewestbuf( void);
  201. #endif
  202.  
  203. #if defined( Desk__using_SDLS) && !defined( Desk__making_Jump)
  204.     #define    Desk_jumpauto_newestbuf    (*Desk_Jump__Ref_autonewestbuf())
  205. #else
  206.     extern    Desk_jumpauto_buf    *Desk_jumpauto_newestbuf;
  207. /*
  208. Always points to the most recent Desk_jumpauto_buf. Is NULL initialy.
  209.  */
  210. #endif
  211.  
  212.  
  213. #define    Desk_JumpAuto__EscapeFromNestedTryUsingReturnOrBreak()    \
  214.     (Desk_jumpauto_newestbuf!=&Desk_jumpauto__localbuf)
  215. /*
  216. A macro that detects list corruption (probably caused by breaking or
  217. returning from a try block).
  218.  */
  219.  
  220.  
  221. #define    Desk_JumpAuto__Push( jumpautobuf)            \
  222.     (jumpautobuf).previous = Desk_jumpauto_newestbuf;    \
  223.     Desk_jumpauto_newestbuf = &(jumpautobuf)
  224. /*
  225. This makes subsequent Desk_JumpAuto_LongJmp's go to 'jumpautobuf', and stores
  226. where they were originally going, so that this place can be restored
  227. later.
  228.  */
  229.  
  230. #define    Desk_JumpAuto__Pop( jumpautobuf)        \
  231.     Desk_jumpauto_newestbuf = (jumpautobuf).previous
  232. /*
  233. Makes subsequent Desk_JumpAuto_LongJmp's go to whatever was the previous
  234. Desk_JumpAuto_SetJmp point (usually this will be in a parent function of the
  235. present function).
  236.  */
  237.  
  238.  
  239. #define    Desk_JumpAuto_Try                                \
  240.     {                                        \
  241.     Desk_jumpauto_buf    Desk_jumpauto__localbuf;                \
  242.     volatile int    Desk_jumpauto_val;                        \
  243.     Desk_JumpAuto__Push( Desk_jumpauto__localbuf);                    \
  244.     Desk_jumpauto_val = Desk_Jump_SetJmp( Desk_jumpauto__localbuf.jumpbuf);        \
  245.     /*Desk_Debug_PrintMemory( Desk_jumpauto_newestbuf, sizeof( Desk_jump_buf));*/    \
  246.     if (!Desk_jumpauto_val)    {
  247. /*
  248. See Desk_JumpAuto_TryCatch
  249.  */
  250.  
  251. #define    Desk_JumpAuto_Catch                                    \
  252.         /* Clean up after try code has finished succesfully:    */        \
  253.         /*Desk_Debug_Assert( !Desk_JumpAuto__EscapeFromNestedTryUsingReturnOrBreak());*/    \
  254.         Desk_JumpAuto__Pop( Desk_jumpauto__localbuf);                    \
  255.         }                                    \
  256.     else    {                                    \
  257.         /* Clean up after a longjmp:    */                    \
  258.         Desk_JumpAuto__Pop( Desk_jumpauto__localbuf);                    \
  259.         Desk_Jump_ReceiveLongJmp( Desk_jumpauto__localbuf.jumpbuf);
  260. /*
  261. See Desk_JumpAuto_TryCatch
  262.  */
  263.  
  264.  
  265. #define    Desk_JumpAuto_EndCatch    \
  266.         }        \
  267.     }
  268. /*
  269. See Desk_JumpAuto_TryCatch
  270.  */
  271.  
  272.  
  273.  
  274. #define    Desk_JumpAuto_Throw( val)        Desk_Jump_LongJmp( Desk_jumpauto_newestbuf->jumpbuf, val)
  275. /*
  276. Behaves like longjmp, jmping to the most recent Desk_JumpAuto_SetJmp place.
  277. 'val' could be a pointer to an error structure such as an Desk_error2_block
  278. (cast to an int). Note that this structure must not be on the stack.
  279.  
  280. It is up to the receiving code to call Desk_JumpAutp__Pop, so that subsequent
  281. Desk_JumpAuto_Throw's go somewhere sensible.
  282.  */
  283.  
  284.  
  285.  
  286. #define Desk_JumpAuto_TryCatch( trycode, catchcode)    \
  287.     Desk_JumpAuto_Try    { trycode    }    \
  288.     Desk_JumpAuto_Catch    { catchcode    }    \
  289.     Desk_JumpAuto_EndCatch
  290. /*
  291. Purpose
  292. -------
  293. This macro, along with Desk_JumpAuto_Throw, allows you to write pseudo
  294. try...catch code without worrying about the other Jump* macros.
  295.  
  296. Acorn's cc will warn about 'more then 10 lines of macro argument' if
  297. your 'trycode' and 'catchcode' have many statements.
  298.  
  299.  
  300. Example code
  301. ------------
  302.  
  303.     void    Foo( void)
  304.     {
  305.     if (...)    Desk_JumpAuto_Throw( 2);
  306.     }
  307.     
  308.     void    Bar( void)
  309.     {
  310.     if (...)    Desk_JumpAuto_Throw( 1);
  311.     }
  312.     
  313.     void    Somefunction( void)
  314.     {
  315.     Desk_JumpAuto_TryCatch(
  316.         Foo();
  317.         Bar();    [DO NOT 'return' or 'break' out of this block]
  318.         ,
  319.         printf( "Foo() or Bar() failed with longjmp( %i)...\n", Desk_jumpauto_val);
  320.         )
  321.     }
  322.  
  323. An alternative way of doing the same thing is to use the Desk_JumpAuto_Try,
  324. Desk_JumpAuto_Catch and Desk_JumpAuto_EndCatch macros individually:
  325.  
  326.     Desk_JumpAuto_Try    {
  327.         Foo();
  328.         Bar();    [DO NOT 'return' or 'break' out of this block]
  329.         }
  330.     Desk_JumpAuto_Catch    {
  331.         printf( "Foo() or Bar() failed with longjmp( %i)...\n", Desk_jumpauto_val);
  332.         }
  333.     Desk_JumpAuto_EndCatch
  334.  
  335.  
  336. The value passed to 'Desk_JumpAuto_Throw' is available to 'catchcode' as the
  337. local int 'Desk_jumpauto_val'. When Desk_JumpAuto_Error2Handler is used,
  338. Desk_jumpauto_val will be the Desk_error2_block* (cast into an integer).
  339.  
  340. This means that, if you are using the Error2 system with
  341. Desk_JumpAuto_Error2Handler as the error handler, you could do:
  342.  
  343. Desk_JumpAuto_TryCatch(
  344.     ...
  345.     ,
  346.     printf( "Error occurred, address of Desk_error2_block is %p\n", 
  347.         (Desk_error2_block*) Desk_jumpauto_val
  348.         )
  349.     )
  350.  
  351.  
  352.  
  353.  
  354. Notes
  355. =====
  356.  
  357.  
  358. 'break' and 'return' with Desk_JumpAuto_*
  359. ------------------------------------
  360.  
  361. You should *never* leave the 'try' code with a break, return, goto,
  362. longjmp or similar.
  363.  
  364. In fact, control should only leave a try block in one of two ways: By
  365. reaching the end of the try block and 'falling through', or by an
  366. exception being thrown.
  367.  
  368. The reason for this restriction is that the JumpAuto system needs to be
  369. told to remove a jmp_buf from its list of jmp_bufs when a 'try' block is
  370. exited, and this is only done by the 'JumpAuto_Catch' macro. If you
  371. accidently leave a 'try' block with a 'return' or similar, subsequent
  372. calls to Desk_JumpAuto_Throw will cause horrible crashes - the system
  373. will jump to a non-existant jmp_buf.
  374.  
  375.  
  376. Terminating ';'
  377. ---------------
  378.  
  379. When using the Desk_JumpAuto_TryCatch macro, you shouldn't use a terminating
  380. ';'.
  381.  
  382. This makes use of Desk_JumpAuto_TryCatch look rather un C-like. It would be
  383. possible to use a 'do ... while (0)' construct in the Desk_JumpAuto_TryCatch
  384. macro (as used in Desk_JumpAuto_ReturnValue, for eg), but this doesn't work
  385. too well if 'catchcode' is exited with a call to 'break' (using break in
  386. this way in 'trycode' is not allowed anyway).
  387.  
  388. An alternative way of accepting a terminating ';' is to use 'if (1)
  389. {...} else', but this will create really bizarre and confusing errors if
  390. the ';' is forgoton.
  391.  
  392.  
  393. Compiler errors
  394. ---------------
  395.  
  396. Because the Desk_JumpAuto_ macros are not part of the language, the compiler
  397. cannot check the syntax. If you misuse them you will generally get some
  398. weird errors.
  399.  
  400.  
  401. Automatic variables
  402. -------------------
  403.  
  404. Unless they are declared as 'volatile', automatic variables that are
  405. modified within the try block will have an undefined value after
  406. the try block if a longjmp is made.
  407.  
  408. For example:
  409.  
  410.     {
  411.     int i = 1;    // automatic variable
  412.     
  413.     Desk_JumpAuto_Try
  414.         {
  415.         i = 2;    // modified in try block
  416.         if (error) Desk_JumpAuto_Throw( 3);
  417.         }
  418.     Desk_JumpAuto_Catch
  419.         {
  420.         'i' is undefined here
  421.         }
  422.     Desk_JumpAuto_EndCatch
  423.     
  424.     i is undefined here if a longjmp was made.
  425.     }
  426.  
  427. so do:
  428.  
  429.     {
  430.     char volatile *s = NULL;    // automatic variable
  431.     
  432.     Desk_JumpAuto_Try
  433.         {
  434.         s = ...;    // modified in try block
  435.         if (error) Desk_JumpAuto_Throw( 3);
  436.         }
  437.     Desk_JumpAuto_Catch
  438.         {
  439.         's' is defined here
  440.         }
  441.     Desk_JumpAuto_EndCatch
  442.     
  443.     i is defined here even if a longjmp was made.
  444.     }
  445.  
  446.  
  447.  */
  448.  
  449.  
  450.  
  451.  
  452.  
  453.  
  454.  
  455.  
  456.  
  457. #ifdef __cplusplus
  458.     }
  459. #endif
  460.  
  461. #endif
  462.